﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using VtcPositionDemo.Common.Models;
using VtcPositionDemo.Common.MvvmLight;


//https://docs.microsoft.com/zh-cn/dotnet/api/system.net.httplistener?view=netframework-4.7.2
namespace VtcPositionDemo.Common.Utils
{

    public class Result
    {
        public int code { get; set; }
        public object data { get; set; }
        public string msg { get; set; }
        public static  Result successResult(Object obj)
        {
            Result  r= new Result();
            r.msg = "success";
            r.data = obj;
            return r;
        }
    }




    public class Request
    {

        public HttpListenerContext httpListenerContext;

        public Object param;

        public String path;

    }


    public class Response
    {

        public HttpListenerContext httpListenerContext;
       

        public void send(Result json)
        {

            httpListenerContext.Response.AddHeader("Access-Control-Allow-Origin", "*");
            httpListenerContext.Response.AddHeader("Access-Control-Allow-Headers", "*");
            httpListenerContext.Response.AddHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
            httpListenerContext.Response.AddHeader("Access-Control-Allow-Credentials", "true");
            httpListenerContext.Response.ContentType = "application/json;charset=utf-8";
            string str=  JsonConvert.SerializeObject(json);
            byte[] msg = System.Text.Encoding.Default.GetBytes(str);
            httpListenerContext.Response.ContentLength64 = msg.Length;
            using (Stream s = httpListenerContext.Response.OutputStream)
            {
                s.Write(msg, 0, msg.Length);
            }
        }

        public void send(String str)
        {
            byte[] msg = Encoding.Default.GetBytes(str);
            httpListenerContext.Response.ContentLength64 = msg.Length;
            using (Stream s = httpListenerContext.Response.OutputStream)
            {
                s.Write(msg, 0, msg.Length);
            }
        }
    }

        public class MingHttpServerUtils
        {
            public static MingHttpServerUtils app = new MingHttpServerUtils();
            // HTTP服务
            private HttpListener listener = new HttpListener();
            private Thread ThreadListener = null;
            public Dictionary<String, String> M = new Dictionary<string, string>();
            public Dictionary<String, Action<Request, Response>> M_GET = new Dictionary<string, Action<Request, Response>>();
            public Dictionary<String, Action<Request, Response>> M_POST = new Dictionary<string, Action<Request, Response>>();
            public Dictionary<String, Action<Request, Response>> M_MAPPING = new Dictionary<string, Action<Request, Response>>();

            public void set(String k, String v)
            {
                M[k] = v;
            }

            public void get(String path, Action<Request, Response> action)
            {
                M_GET[path] = action;
            }

            public void post(String path, Action<Request, Response> action)
            {
                M_POST[path] = action;
            }


            // 自定义POST处理
            public delegate string EventDo(object sender, HttpListenerRequest request);
            public event EventDo OnEventDo;

            // 消息事件
            public delegate void EventInfo(object sender, string info);
            public event EventInfo OnEventInfo;

            // 最后错误信息
            public string LastErrorInfo = "";

            // 是否调试
            public bool IsDebug = false;

            public MingHttpServerUtils()
            {
            }

            // 触发消息
            private void showInfo(string str)
            {
                OnEventInfo(this, str);
            }

            public void init()
            {
                this.set("_views", AppDomain.CurrentDomain.BaseDirectory + "www\\");
                M_MAPPING["_no_router"]= (req, res) =>
                {
                    res.send("no_router");
                };
            }


        public Boolean isStaticRequest(String url)
        {
            if (url.Contains(".html") ||
                   url.Contains(".css") ||
                    url.Contains(".js") ||
                        url.Contains(".jpg") ||
                            url.Contains(".png") ||
                                url.Contains(".htm") ||
                                   url.Contains(".txt")

                   )
            {
                return true;
            }
            else
            {
                return false;
            }
        }



            // Http服务器启动
            public bool listen(int port)
            {
                init();
                HttpListener listener = new HttpListener();
                listener.Prefixes.Add($"http://localhost:{port}/");
                listener.Start();
                Console.WriteLine();

                app.get("/mi", (req, res) =>
                {
                    res.send(Result.successResult("AAA"));
                });


                app.post("/mip", (req, res) =>
                {
                    res.send(Result.successResult("AAA"));
                });

            app.get("/sseServer", (req, res) =>
                {
                    HttpListenerContext httpListenerContext = req.httpListenerContext;
                    httpListenerContext.Response.AddHeader("Cache-Control", "no-cache");
                    httpListenerContext.Response.AddHeader("Connection", "keep-alive");
                    httpListenerContext.Response.ContentType = "text/event-stream";

                    new Thread(() =>
                    {
                        using (Stream s = httpListenerContext.Response.OutputStream)
                        {
                            try
                            {
                                for (int i = 0; i < 50; i++)
                                {
                                    Console.WriteLine(httpListenerContext);
                                    if (httpListenerContext.Request == null)
                                    {
                                        return;
                                    }
                                    byte[] msg = Encoding.Default.GetBytes($"id: {i} \ndata: aa{i} \nretry: 100000 \n\n\n");
                                    s.Write(msg, 0, msg.Length);
                                    Thread.Sleep(1000);
                                }
                            }
                            catch (Exception e)
                            {

                            }
                        }
                    }).Start();

                });

                Messenger.Send<LogMsg>(LogMsg.HttpServerLog("Listening on " + port));
                Task task = Task.Factory.StartNew(() =>
                {
                    while (listener.IsListening)
                    {
                        HttpListenerContext context = listener.GetContext();
                        Console.WriteLine("Http requesting...");
                        HttpListenerRequest request = context.Request;
                        HttpListenerResponse response = context.Response;
                        String path= request.RawUrl.Split(new char[1] { '?' })[0];
                        Request request1 = new Request();
                        request1.path = path;
                        request1.httpListenerContext = context;
                        Response response1 = new Response();
                        response1.httpListenerContext = context;
                        if (isStaticRequest(path)){
                            StaticServer(request1, response1);
                        }
                        else {
                            ApiServerServer(request1, response1);
                        }
                    }
                });
                task.Wait();
                return true;
            }

            // 停止
            public void stop()
            {
                if (listener.IsListening)
                {
                    ThreadListener.Abort();
                    listener.Stop();
                    ThreadListener = null;
                }
            }

            // 是否在运行
            public bool isRun()
            {
                return listener.IsListening;
            }

            // 处理用户静态请求
            private void StaticServer(Request request, Response response)
            {
                var context = request.httpListenerContext;
                try
                {
                    string filename = context.Request.Url.AbsolutePath.Trim('/');
                     filename = filename.Replace("www/", "");
                    if (filename == "")
                    {
                        filename = "index.html";
                    }
                    if (IsDebug)
                    {
                        showInfo(context.Request.Url.ToString());
                    }
                    string[] ext_list = filename.Split('.');
                    string ext = "";
                    if (ext_list.Length > 1)
                    {
                        ext = ext_list[ext_list.Length - 1];
                    }
                    String qa = M["_views"];
                    string path = Path.Combine(M["_views"], filename);
                    byte[] msg = new byte[0];

                    context.Response.StatusCode = (int)HttpStatusCode.OK;

                    string expires = DateTime.Now.AddYears(10).ToString("r");

                    // 浏览器缓存
                    switch (ext)
                    {
                        case "html":
                        case "htm":
                            context.Response.ContentType = "text/html";
                            break;
                        case "jpg":
                        case "jpeg":
                        case "jpe":
                            context.Response.ContentType = "image/jpeg";
                            context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
                            context.Response.AddHeader("expires", expires);
                            break;
                        case "png":
                            context.Response.ContentType = "image/png";
                            context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
                            context.Response.AddHeader("expires", expires);
                            break;
                        case "gif":
                            context.Response.ContentType = "image/gif";
                            context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
                            context.Response.AddHeader("expires", expires);
                            break;
                        case "js":
                            context.Response.ContentType = "application/x-javascript";
                            context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
                            context.Response.AddHeader("expires", expires);
                            break;
                        case "css":
                            context.Response.ContentType = "text/css";
                            context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
                            context.Response.AddHeader("expires", expires);
                            break;
                        case "ico":
                            context.Response.ContentType = "application/x-ico";
                            context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
                            context.Response.AddHeader("expires", expires);
                            break;
                        case "txt":
                            context.Response.ContentType = "text/plain";
                            break;
                        case "do":
                            context.Response.AddHeader("Access-Control-Allow-Origin", "*");
                            context.Response.ContentType = "text/plain;charset=utf-8";
                            string strDo = OnEventDo(this, context.Request);
                            msg = Encoding.UTF8.GetBytes(strDo);
                            break;
                        default:
                            context.Response.ContentType = "";
                            context.Response.AddHeader("cache-control", "max-age=315360000, immutable");
                            context.Response.AddHeader("expires", expires);
                            break;
                    }

                    if (msg.Length == 0)
                    {
                         path= path.Replace("/","\\");
                        if (!File.Exists(path))
                        {
                            context.Response.ContentType = "text/html";
                            context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                            if (File.Exists(M["_views"] + "error.html"))
                            {
                                msg = File.ReadAllBytes(M["_views"] + "error.html");
                            }
                            else
                            {
                                msg = Encoding.Default.GetBytes("404");
                            }
                        }
                        else
                        {
                            msg = File.ReadAllBytes(path);
                        }
                    }
                    context.Response.ContentLength64 = msg.Length;
                    using (Stream s = context.Response.OutputStream)
                    {
                        s.Write(msg, 0, msg.Length);
                    }

                    msg = new byte[0];
                    GC.Collect();
                }
                catch (Exception ex)
                {
                    showInfo("ERROR:" + ex.Message);
                }
            }

            private void ApiServerServer(Request request,Response response)
            {
                String methed = request.httpListenerContext.Request.HttpMethod;
                HttpListenerRequest lReq = request.httpListenerContext.Request;
                 NameValueCollection nameValueCollection = lReq.QueryString;
                 JObject param = new JObject();  
                 foreach (var k in nameValueCollection.AllKeys)
                 {
                    String v = nameValueCollection.Get(k);
                    param.Add(k, v);
                 }

                request.param= param;


            if ("POST".Equals(methed))
            {
                String bodyContent = "";
                if (request.httpListenerContext.Request.HasEntityBody)
                {
                    Stream stream = request.httpListenerContext.Request.InputStream;
                    StreamReader reader = new StreamReader(stream, Encoding.UTF8);
                    bodyContent = reader.ReadToEnd();
                    if (bodyContent.Length > 2)
                    {
                        request.param = JsonConvert.DeserializeObject(bodyContent);
                    }
               
                }
            }

            try
                {
                    if (true)
                    {
                        if ("GET".Equals(methed))
                        {
                            if (M_GET.ContainsKey(request.path))
                            {
                                M_GET[request.path].Invoke(request, response);
                                return;
                            }
                            else
                            {
                                M_MAPPING["_no_router"].Invoke(request, response);
                            }

                            return;
                        }

                        if ("POST".Equals(methed))
                        {
                            if (M_GET.ContainsKey(request.path))
                            {
                                M_GET[request.path].Invoke(request, response);
                                return;
                            }
                            else
                            {
                                M_MAPPING["_no_router"].Invoke(request, response);
                            }

                            return;
                        }

                        Console.WriteLine(methed);
                    }
                    response.send(Result.successResult("404"));
                    GC.Collect();
                }
                catch (Exception ex)
                {
                    showInfo("ERROR:" + ex.Message);
                }
            }


        }
    }

